// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_ARM64_REGISTER_ARM64_H_
#define V8_ARM64_REGISTER_ARM64_H_

#include "src/arm64/utils-arm64.h"
#include "src/globals.h"
#include "src/register.h"
#include "src/reglist.h"

namespace v8 {
namespace internal {

// -----------------------------------------------------------------------------
// Registers.
// clang-format on
#define GENERAL_REGISTER_CODE_LIST(R)                       \
    R(0)                                                    \
    R(1) R(2) R(3) R(4) R(5) R(6) R(7)                      \
        R(8) R(9) R(10) R(11) R(12) R(13) R(14) R(15)       \
            R(16) R(17) R(18) R(19) R(20) R(21) R(22) R(23) \
                R(24) R(25) R(26) R(27) R(28) R(29) R(30) R(31)

#define GENERAL_REGISTERS(R)                                        \
    R(x0)                                                           \
    R(x1) R(x2) R(x3) R(x4) R(x5) R(x6) R(x7)                       \
        R(x8) R(x9) R(x10) R(x11) R(x12) R(x13) R(x14) R(x15)       \
            R(x16) R(x17) R(x18) R(x19) R(x20) R(x21) R(x22) R(x23) \
                R(x24) R(x25) R(x26) R(x27) R(x28) R(x29) R(x30) R(x31)

// x18 is the platform register and is reserved for the use of platform ABIs.
// It is known to be reserved by the OS at least on Windows and iOS.
#define ALLOCATABLE_GENERAL_REGISTERS(R)                      \
    R(x0)                                                     \
    R(x1) R(x2) R(x3) R(x4) R(x5) R(x6) R(x7)                 \
        R(x8) R(x9) R(x10) R(x11) R(x12) R(x13) R(x14) R(x15) \
            R(x19) R(x20) R(x21) R(x22) R(x23) R(x24) R(x25)  \
                R(x27) R(x28)

#define FLOAT_REGISTERS(V)                                          \
    V(s0)                                                           \
    V(s1) V(s2) V(s3) V(s4) V(s5) V(s6) V(s7)                       \
        V(s8) V(s9) V(s10) V(s11) V(s12) V(s13) V(s14) V(s15)       \
            V(s16) V(s17) V(s18) V(s19) V(s20) V(s21) V(s22) V(s23) \
                V(s24) V(s25) V(s26) V(s27) V(s28) V(s29) V(s30) V(s31)

#define DOUBLE_REGISTERS(R)                                         \
    R(d0)                                                           \
    R(d1) R(d2) R(d3) R(d4) R(d5) R(d6) R(d7)                       \
        R(d8) R(d9) R(d10) R(d11) R(d12) R(d13) R(d14) R(d15)       \
            R(d16) R(d17) R(d18) R(d19) R(d20) R(d21) R(d22) R(d23) \
                R(d24) R(d25) R(d26) R(d27) R(d28) R(d29) R(d30) R(d31)

#define SIMD128_REGISTERS(V)                                        \
    V(q0)                                                           \
    V(q1) V(q2) V(q3) V(q4) V(q5) V(q6) V(q7)                       \
        V(q8) V(q9) V(q10) V(q11) V(q12) V(q13) V(q14) V(q15)       \
            V(q16) V(q17) V(q18) V(q19) V(q20) V(q21) V(q22) V(q23) \
                V(q24) V(q25) V(q26) V(q27) V(q28) V(q29) V(q30) V(q31)

#define VECTOR_REGISTERS(V)                                         \
    V(v0)                                                           \
    V(v1) V(v2) V(v3) V(v4) V(v5) V(v6) V(v7)                       \
        V(v8) V(v9) V(v10) V(v11) V(v12) V(v13) V(v14) V(v15)       \
            V(v16) V(v17) V(v18) V(v19) V(v20) V(v21) V(v22) V(v23) \
                V(v24) V(v25) V(v26) V(v27) V(v28) V(v29) V(v30) V(v31)

// Register d29 could be allocated, but we keep an even length list here, in
// order to make stack alignment easier for save and restore.
#define ALLOCATABLE_DOUBLE_REGISTERS(R)                             \
    R(d0)                                                           \
    R(d1) R(d2) R(d3) R(d4) R(d5) R(d6) R(d7)                       \
        R(d8) R(d9) R(d10) R(d11) R(d12) R(d13) R(d14) R(d16)       \
            R(d17) R(d18) R(d19) R(d20) R(d21) R(d22) R(d23) R(d24) \
                R(d25) R(d26) R(d27) R(d28)
    // clang-format on

    constexpr int kRegListSizeInBits = sizeof(RegList) * kBitsPerByte;

    const int kNumRegs = kNumberOfRegisters;
    // Registers x0-x17 are caller-saved.
    const int kNumJSCallerSaved = 18;
    const RegList kJSCallerSaved = 0x3ffff;

    // Number of registers for which space is reserved in safepoints. Must be a
    // multiple of eight.
    // TODO(all): Refine this number.
    const int kNumSafepointRegisters = 32;

// Define the list of registers actually saved at safepoints.
// Note that the number of saved registers may be smaller than the reserved
// space, i.e. kNumSafepointSavedRegisters <= kNumSafepointRegisters.
#define kSafepointSavedRegisters CPURegList::GetSafepointSavedRegisters().list()
#define kNumSafepointSavedRegisters \
    CPURegList::GetSafepointSavedRegisters().Count()

    // Some CPURegister methods can return Register and VRegister types, so we
    // need to declare them in advance.
    class Register;
    class VRegister;

    enum RegisterCode {
#define REGISTER_CODE(R) kRegCode_##R,
        GENERAL_REGISTERS(REGISTER_CODE)
#undef REGISTER_CODE
            kRegAfterLast
    };

    class CPURegister : public RegisterBase<CPURegister, kRegAfterLast> {
    public:
        enum RegisterType { kRegister,
            kVRegister,
            kNoRegister };

        static constexpr CPURegister no_reg()
        {
            return CPURegister { 0, 0, kNoRegister };
        }

        template <int code, int size, RegisterType type>
        static constexpr CPURegister Create()
        {
            static_assert(IsValid(code, size, type), "Cannot create invalid registers");
            return CPURegister { code, size, type };
        }

        static CPURegister Create(int code, int size, RegisterType type)
        {
            DCHECK(IsValid(code, size, type));
            return CPURegister { code, size, type };
        }

        RegisterType type() const { return reg_type_; }
        int SizeInBits() const
        {
            DCHECK(IsValid());
            return reg_size_;
        }
        int SizeInBytes() const
        {
            DCHECK(IsValid());
            DCHECK_EQ(SizeInBits() % 8, 0);
            return reg_size_ / 8;
        }
        bool Is8Bits() const
        {
            DCHECK(IsValid());
            return reg_size_ == 8;
        }
        bool Is16Bits() const
        {
            DCHECK(IsValid());
            return reg_size_ == 16;
        }
        bool Is32Bits() const
        {
            DCHECK(IsValid());
            return reg_size_ == 32;
        }
        bool Is64Bits() const
        {
            DCHECK(IsValid());
            return reg_size_ == 64;
        }
        bool Is128Bits() const
        {
            DCHECK(IsValid());
            return reg_size_ == 128;
        }
        bool IsValid() const { return reg_type_ != kNoRegister; }
        bool IsNone() const { return reg_type_ == kNoRegister; }
        bool Is(const CPURegister& other) const
        {
            return Aliases(other) && (reg_size_ == other.reg_size_);
        }
        bool Aliases(const CPURegister& other) const
        {
            return (reg_code_ == other.reg_code_) && (reg_type_ == other.reg_type_);
        }

        bool IsZero() const;
        bool IsSP() const;

        bool IsRegister() const { return reg_type_ == kRegister; }
        bool IsVRegister() const { return reg_type_ == kVRegister; }

        bool IsFPRegister() const { return IsS() || IsD(); }

        bool IsW() const { return IsRegister() && Is32Bits(); }
        bool IsX() const { return IsRegister() && Is64Bits(); }

        // These assertions ensure that the size and type of the register are as
        // described. They do not consider the number of lanes that make up a vector.
        // So, for example, Is8B() implies IsD(), and Is1D() implies IsD, but IsD()
        // does not imply Is1D() or Is8B().
        // Check the number of lanes, ie. the format of the vector, using methods such
        // as Is8B(), Is1D(), etc. in the VRegister class.
        bool IsV() const { return IsVRegister(); }
        bool IsB() const { return IsV() && Is8Bits(); }
        bool IsH() const { return IsV() && Is16Bits(); }
        bool IsS() const { return IsV() && Is32Bits(); }
        bool IsD() const { return IsV() && Is64Bits(); }
        bool IsQ() const { return IsV() && Is128Bits(); }

        Register Reg() const;
        VRegister VReg() const;

        Register X() const;
        Register W() const;
        VRegister V() const;
        VRegister B() const;
        VRegister H() const;
        VRegister D() const;
        VRegister S() const;
        VRegister Q() const;

        bool IsSameSizeAndType(const CPURegister& other) const;

        bool is(const CPURegister& other) const { return Is(other); }
        bool is_valid() const { return IsValid(); }

    protected:
        int reg_size_;
        RegisterType reg_type_;

#if defined(V8_OS_WIN) && !defined(__clang__)
        // MSVC has problem to parse template base class as friend class.
        friend RegisterBase;
#else
        friend class RegisterBase;
#endif

        constexpr CPURegister(int code, int size, RegisterType type)
            : RegisterBase(code)
            , reg_size_(size)
            , reg_type_(type)
        {
        }

        static constexpr bool IsValidRegister(int code, int size)
        {
            return (size == kWRegSizeInBits || size == kXRegSizeInBits) && (code < kNumberOfRegisters || code == kSPRegInternalCode);
        }

        static constexpr bool IsValidVRegister(int code, int size)
        {
            return (size == kBRegSizeInBits || size == kHRegSizeInBits || size == kSRegSizeInBits || size == kDRegSizeInBits || size == kQRegSizeInBits) && code < kNumberOfVRegisters;
        }

        static constexpr bool IsValid(int code, int size, RegisterType type)
        {
            return (type == kRegister && IsValidRegister(code, size)) || (type == kVRegister && IsValidVRegister(code, size));
        }

        static constexpr bool IsNone(int code, int size, RegisterType type)
        {
            return type == kNoRegister && code == 0 && size == 0;
        }
    };

    ASSERT_TRIVIALLY_COPYABLE(CPURegister);

    class Register : public CPURegister {
    public:
        static constexpr Register no_reg() { return Register(CPURegister::no_reg()); }

        template <int code, int size>
        static constexpr Register Create()
        {
            return Register(CPURegister::Create<code, size, CPURegister::kRegister>());
        }

        static Register Create(int code, int size)
        {
            return Register(CPURegister::Create(code, size, CPURegister::kRegister));
        }

        static Register XRegFromCode(unsigned code);
        static Register WRegFromCode(unsigned code);

        static Register from_code(int code)
        {
            // Always return an X register.
            return Register::Create(code, kXRegSizeInBits);
        }

        template <int code>
        static Register from_code()
        {
            // Always return an X register.
            return Register::Create<code, kXRegSizeInBits>();
        }

        static const char* GetSpecialRegisterName(int code)
        {
            return (code == kSPRegInternalCode) ? "sp" : "UNKNOWN";
        }

    private:
        constexpr explicit Register(const CPURegister& r)
            : CPURegister(r)
        {
        }
    };

    ASSERT_TRIVIALLY_COPYABLE(Register);

    constexpr bool kPadArguments = true;
    constexpr bool kSimpleFPAliasing = true;
    constexpr bool kSimdMaskRegisters = false;

    enum DoubleRegisterCode {
#define REGISTER_CODE(R) kDoubleCode_##R,
        DOUBLE_REGISTERS(REGISTER_CODE)
#undef REGISTER_CODE
            kDoubleAfterLast
    };

    // Functions for handling NEON vector format information.
    enum VectorFormat {
        kFormatUndefined = 0xffffffff,
        kFormat8B = NEON_8B,
        kFormat16B = NEON_16B,
        kFormat4H = NEON_4H,
        kFormat8H = NEON_8H,
        kFormat2S = NEON_2S,
        kFormat4S = NEON_4S,
        kFormat1D = NEON_1D,
        kFormat2D = NEON_2D,

        // Scalar formats. We add the scalar bit to distinguish between scalar and
        // vector enumerations; the bit is always set in the encoding of scalar ops
        // and always clear for vector ops. Although kFormatD and kFormat1D appear
        // to be the same, their meaning is subtly different. The first is a scalar
        // operation, the second a vector operation that only affects one lane.
        kFormatB = NEON_B | NEONScalar,
        kFormatH = NEON_H | NEONScalar,
        kFormatS = NEON_S | NEONScalar,
        kFormatD = NEON_D | NEONScalar
    };

    VectorFormat VectorFormatHalfWidth(VectorFormat vform);
    VectorFormat VectorFormatDoubleWidth(VectorFormat vform);
    VectorFormat VectorFormatDoubleLanes(VectorFormat vform);
    VectorFormat VectorFormatHalfLanes(VectorFormat vform);
    VectorFormat ScalarFormatFromLaneSize(int lanesize);
    VectorFormat VectorFormatHalfWidthDoubleLanes(VectorFormat vform);
    VectorFormat VectorFormatFillQ(VectorFormat vform);
    VectorFormat ScalarFormatFromFormat(VectorFormat vform);
    V8_EXPORT_PRIVATE unsigned RegisterSizeInBitsFromFormat(VectorFormat vform);
    unsigned RegisterSizeInBytesFromFormat(VectorFormat vform);
    int LaneSizeInBytesFromFormat(VectorFormat vform);
    unsigned LaneSizeInBitsFromFormat(VectorFormat vform);
    int LaneSizeInBytesLog2FromFormat(VectorFormat vform);
    V8_EXPORT_PRIVATE int LaneCountFromFormat(VectorFormat vform);
    int MaxLaneCountFromFormat(VectorFormat vform);
    V8_EXPORT_PRIVATE bool IsVectorFormat(VectorFormat vform);
    int64_t MaxIntFromFormat(VectorFormat vform);
    int64_t MinIntFromFormat(VectorFormat vform);
    uint64_t MaxUintFromFormat(VectorFormat vform);

    class VRegister : public CPURegister {
    public:
        static constexpr VRegister no_reg()
        {
            return VRegister(CPURegister::no_reg(), 0);
        }

        template <int code, int size, int lane_count = 1>
        static constexpr VRegister Create()
        {
            static_assert(IsValidLaneCount(lane_count), "Invalid lane count");
            return VRegister(CPURegister::Create<code, size, kVRegister>(), lane_count);
        }

        static VRegister Create(int code, int size, int lane_count = 1)
        {
            DCHECK(IsValidLaneCount(lane_count));
            return VRegister(CPURegister::Create(code, size, CPURegister::kVRegister),
                lane_count);
        }

        static VRegister Create(int reg_code, VectorFormat format)
        {
            int reg_size = RegisterSizeInBitsFromFormat(format);
            int reg_count = IsVectorFormat(format) ? LaneCountFromFormat(format) : 1;
            return VRegister::Create(reg_code, reg_size, reg_count);
        }

        static VRegister BRegFromCode(unsigned code);
        static VRegister HRegFromCode(unsigned code);
        static VRegister SRegFromCode(unsigned code);
        static VRegister DRegFromCode(unsigned code);
        static VRegister QRegFromCode(unsigned code);
        static VRegister VRegFromCode(unsigned code);

        VRegister V8B() const
        {
            return VRegister::Create(code(), kDRegSizeInBits, 8);
        }
        VRegister V16B() const
        {
            return VRegister::Create(code(), kQRegSizeInBits, 16);
        }
        VRegister V4H() const
        {
            return VRegister::Create(code(), kDRegSizeInBits, 4);
        }
        VRegister V8H() const
        {
            return VRegister::Create(code(), kQRegSizeInBits, 8);
        }
        VRegister V2S() const
        {
            return VRegister::Create(code(), kDRegSizeInBits, 2);
        }
        VRegister V4S() const
        {
            return VRegister::Create(code(), kQRegSizeInBits, 4);
        }
        VRegister V2D() const
        {
            return VRegister::Create(code(), kQRegSizeInBits, 2);
        }
        VRegister V1D() const
        {
            return VRegister::Create(code(), kDRegSizeInBits, 1);
        }

        bool Is8B() const { return (Is64Bits() && (lane_count_ == 8)); }
        bool Is16B() const { return (Is128Bits() && (lane_count_ == 16)); }
        bool Is4H() const { return (Is64Bits() && (lane_count_ == 4)); }
        bool Is8H() const { return (Is128Bits() && (lane_count_ == 8)); }
        bool Is2S() const { return (Is64Bits() && (lane_count_ == 2)); }
        bool Is4S() const { return (Is128Bits() && (lane_count_ == 4)); }
        bool Is1D() const { return (Is64Bits() && (lane_count_ == 1)); }
        bool Is2D() const { return (Is128Bits() && (lane_count_ == 2)); }

        // For consistency, we assert the number of lanes of these scalar registers,
        // even though there are no vectors of equivalent total size with which they
        // could alias.
        bool Is1B() const
        {
            DCHECK(!(Is8Bits() && IsVector()));
            return Is8Bits();
        }
        bool Is1H() const
        {
            DCHECK(!(Is16Bits() && IsVector()));
            return Is16Bits();
        }
        bool Is1S() const
        {
            DCHECK(!(Is32Bits() && IsVector()));
            return Is32Bits();
        }

        bool IsLaneSizeB() const { return LaneSizeInBits() == kBRegSizeInBits; }
        bool IsLaneSizeH() const { return LaneSizeInBits() == kHRegSizeInBits; }
        bool IsLaneSizeS() const { return LaneSizeInBits() == kSRegSizeInBits; }
        bool IsLaneSizeD() const { return LaneSizeInBits() == kDRegSizeInBits; }

        bool IsScalar() const { return lane_count_ == 1; }
        bool IsVector() const { return lane_count_ > 1; }

        bool IsSameFormat(const VRegister& other) const
        {
            return (reg_size_ == other.reg_size_) && (lane_count_ == other.lane_count_);
        }

        int LaneCount() const { return lane_count_; }

        unsigned LaneSizeInBytes() const { return SizeInBytes() / lane_count_; }

        unsigned LaneSizeInBits() const { return LaneSizeInBytes() * 8; }

        static constexpr int kMaxNumRegisters = kNumberOfVRegisters;
        STATIC_ASSERT(kMaxNumRegisters == kDoubleAfterLast);

        static VRegister from_code(int code)
        {
            // Always return a D register.
            return VRegister::Create(code, kDRegSizeInBits);
        }

    private:
        int lane_count_;

        constexpr explicit VRegister(const CPURegister& r, int lane_count)
            : CPURegister(r)
            , lane_count_(lane_count)
        {
        }

        static constexpr bool IsValidLaneCount(int lane_count)
        {
            return base::bits::IsPowerOfTwo(lane_count) && lane_count <= 16;
        }
    };

    ASSERT_TRIVIALLY_COPYABLE(VRegister);

    // No*Reg is used to indicate an unused argument, or an error case. Note that
    // these all compare equal (using the Is() method). The Register and VRegister
    // variants are provided for convenience.
    constexpr Register NoReg = Register::no_reg();
    constexpr VRegister NoVReg = VRegister::no_reg();
    constexpr CPURegister NoCPUReg = CPURegister::no_reg();
    constexpr Register no_reg = NoReg;
    constexpr VRegister no_dreg = NoVReg;

#define DEFINE_REGISTER(register_class, name, ...) \
    constexpr register_class name = register_class::Create<__VA_ARGS__>()
#define ALIAS_REGISTER(register_class, alias, name) \
    constexpr register_class alias = name

#define DEFINE_REGISTERS(N)                              \
    DEFINE_REGISTER(Register, w##N, N, kWRegSizeInBits); \
    DEFINE_REGISTER(Register, x##N, N, kXRegSizeInBits);
    GENERAL_REGISTER_CODE_LIST(DEFINE_REGISTERS)
#undef DEFINE_REGISTERS

    DEFINE_REGISTER(Register, wsp, kSPRegInternalCode, kWRegSizeInBits);
    DEFINE_REGISTER(Register, sp, kSPRegInternalCode, kXRegSizeInBits);

#define DEFINE_VREGISTERS(N)                              \
    DEFINE_REGISTER(VRegister, b##N, N, kBRegSizeInBits); \
    DEFINE_REGISTER(VRegister, h##N, N, kHRegSizeInBits); \
    DEFINE_REGISTER(VRegister, s##N, N, kSRegSizeInBits); \
    DEFINE_REGISTER(VRegister, d##N, N, kDRegSizeInBits); \
    DEFINE_REGISTER(VRegister, q##N, N, kQRegSizeInBits); \
    DEFINE_REGISTER(VRegister, v##N, N, kQRegSizeInBits);
    GENERAL_REGISTER_CODE_LIST(DEFINE_VREGISTERS)
#undef DEFINE_VREGISTERS

#undef DEFINE_REGISTER

    // Registers aliases.
    ALIAS_REGISTER(VRegister, v8_, v8); // Avoid conflicts with namespace v8.
    ALIAS_REGISTER(Register, ip0, x16);
    ALIAS_REGISTER(Register, ip1, x17);
    ALIAS_REGISTER(Register, wip0, w16);
    ALIAS_REGISTER(Register, wip1, w17);
    // Root register.
    ALIAS_REGISTER(Register, kRootRegister, x26);
    ALIAS_REGISTER(Register, rr, x26);
    // Context pointer register.
    ALIAS_REGISTER(Register, cp, x27);
    ALIAS_REGISTER(Register, fp, x29);
    ALIAS_REGISTER(Register, lr, x30);
    ALIAS_REGISTER(Register, xzr, x31);
    ALIAS_REGISTER(Register, wzr, w31);

    // Register used for padding stack slots.
    ALIAS_REGISTER(Register, padreg, x31);

    // Keeps the 0 double value.
    ALIAS_REGISTER(VRegister, fp_zero, d15);
    // MacroAssembler fixed V Registers.
    ALIAS_REGISTER(VRegister, fp_fixed1, d28);
    ALIAS_REGISTER(VRegister, fp_fixed2, d29);

    // MacroAssembler scratch V registers.
    ALIAS_REGISTER(VRegister, fp_scratch, d30);
    ALIAS_REGISTER(VRegister, fp_scratch1, d30);
    ALIAS_REGISTER(VRegister, fp_scratch2, d31);

#undef ALIAS_REGISTER

    // AreAliased returns true if any of the named registers overlap. Arguments set
    // to NoReg are ignored. The system stack pointer may be specified.
    bool AreAliased(const CPURegister& reg1, const CPURegister& reg2,
        const CPURegister& reg3 = NoReg,
        const CPURegister& reg4 = NoReg,
        const CPURegister& reg5 = NoReg,
        const CPURegister& reg6 = NoReg,
        const CPURegister& reg7 = NoReg,
        const CPURegister& reg8 = NoReg);

    // AreSameSizeAndType returns true if all of the specified registers have the
    // same size, and are of the same type. The system stack pointer may be
    // specified. Arguments set to NoReg are ignored, as are any subsequent
    // arguments. At least one argument (reg1) must be valid (not NoCPUReg).
    V8_EXPORT_PRIVATE bool AreSameSizeAndType(
        const CPURegister& reg1, const CPURegister& reg2 = NoCPUReg,
        const CPURegister& reg3 = NoCPUReg, const CPURegister& reg4 = NoCPUReg,
        const CPURegister& reg5 = NoCPUReg, const CPURegister& reg6 = NoCPUReg,
        const CPURegister& reg7 = NoCPUReg, const CPURegister& reg8 = NoCPUReg);

    // AreSameFormat returns true if all of the specified VRegisters have the same
    // vector format. Arguments set to NoVReg are ignored, as are any subsequent
    // arguments. At least one argument (reg1) must be valid (not NoVReg).
    bool AreSameFormat(const VRegister& reg1, const VRegister& reg2,
        const VRegister& reg3 = NoVReg,
        const VRegister& reg4 = NoVReg);

    // AreConsecutive returns true if all of the specified VRegisters are
    // consecutive in the register file. Arguments may be set to NoVReg, and if so,
    // subsequent arguments must also be NoVReg. At least one argument (reg1) must
    // be valid (not NoVReg).
    V8_EXPORT_PRIVATE bool AreConsecutive(const VRegister& reg1,
        const VRegister& reg2,
        const VRegister& reg3 = NoVReg,
        const VRegister& reg4 = NoVReg);

    typedef VRegister FloatRegister;
    typedef VRegister DoubleRegister;
    typedef VRegister Simd128Register;

    // -----------------------------------------------------------------------------
    // Lists of registers.
    class V8_EXPORT_PRIVATE CPURegList {
    public:
        template <typename... CPURegisters>
        explicit CPURegList(CPURegister reg0, CPURegisters... regs)
            : list_(CPURegister::ListOf(reg0, regs...))
            , size_(reg0.SizeInBits())
            , type_(reg0.type())
        {
            DCHECK(AreSameSizeAndType(reg0, regs...));
            DCHECK(IsValid());
        }

        CPURegList(CPURegister::RegisterType type, int size, RegList list)
            : list_(list)
            , size_(size)
            , type_(type)
        {
            DCHECK(IsValid());
        }

        CPURegList(CPURegister::RegisterType type, int size, int first_reg,
            int last_reg)
            : size_(size)
            , type_(type)
        {
            DCHECK(
                ((type == CPURegister::kRegister) && (last_reg < kNumberOfRegisters)) || ((type == CPURegister::kVRegister) && (last_reg < kNumberOfVRegisters)));
            DCHECK(last_reg >= first_reg);
            list_ = (1ULL << (last_reg + 1)) - 1;
            list_ &= ~((1ULL << first_reg) - 1);
            DCHECK(IsValid());
        }

        CPURegister::RegisterType type() const
        {
            DCHECK(IsValid());
            return type_;
        }

        RegList list() const
        {
            DCHECK(IsValid());
            return list_;
        }

        inline void set_list(RegList new_list)
        {
            DCHECK(IsValid());
            list_ = new_list;
        }

        // Combine another CPURegList into this one. Registers that already exist in
        // this list are left unchanged. The type and size of the registers in the
        // 'other' list must match those in this list.
        void Combine(const CPURegList& other);

        // Remove every register in the other CPURegList from this one. Registers that
        // do not exist in this list are ignored. The type of the registers in the
        // 'other' list must match those in this list.
        void Remove(const CPURegList& other);

        // Variants of Combine and Remove which take CPURegisters.
        void Combine(const CPURegister& other);
        void Remove(const CPURegister& other1, const CPURegister& other2 = NoCPUReg,
            const CPURegister& other3 = NoCPUReg,
            const CPURegister& other4 = NoCPUReg);

        // Variants of Combine and Remove which take a single register by its code;
        // the type and size of the register is inferred from this list.
        void Combine(int code);
        void Remove(int code);

        // Remove all callee-saved registers from the list. This can be useful when
        // preparing registers for an AAPCS64 function call, for example.
        void RemoveCalleeSaved();

        CPURegister PopLowestIndex();
        CPURegister PopHighestIndex();

        // AAPCS64 callee-saved registers.
        static CPURegList GetCalleeSaved(int size = kXRegSizeInBits);
        static CPURegList GetCalleeSavedV(int size = kDRegSizeInBits);

        // AAPCS64 caller-saved registers. Note that this includes lr.
        // TODO(all): Determine how we handle d8-d15 being callee-saved, but the top
        // 64-bits being caller-saved.
        static CPURegList GetCallerSaved(int size = kXRegSizeInBits);
        static CPURegList GetCallerSavedV(int size = kDRegSizeInBits);

        // Registers saved as safepoints.
        static CPURegList GetSafepointSavedRegisters();

        bool IsEmpty() const
        {
            DCHECK(IsValid());
            return list_ == 0;
        }

        bool IncludesAliasOf(const CPURegister& other1,
            const CPURegister& other2 = NoCPUReg,
            const CPURegister& other3 = NoCPUReg,
            const CPURegister& other4 = NoCPUReg) const
        {
            DCHECK(IsValid());
            RegList list = 0;
            if (!other1.IsNone() && (other1.type() == type_))
                list |= other1.bit();
            if (!other2.IsNone() && (other2.type() == type_))
                list |= other2.bit();
            if (!other3.IsNone() && (other3.type() == type_))
                list |= other3.bit();
            if (!other4.IsNone() && (other4.type() == type_))
                list |= other4.bit();
            return (list_ & list) != 0;
        }

        int Count() const
        {
            DCHECK(IsValid());
            return CountSetBits(list_, kRegListSizeInBits);
        }

        int RegisterSizeInBits() const
        {
            DCHECK(IsValid());
            return size_;
        }

        int RegisterSizeInBytes() const
        {
            int size_in_bits = RegisterSizeInBits();
            DCHECK_EQ(size_in_bits % kBitsPerByte, 0);
            return size_in_bits / kBitsPerByte;
        }

        int TotalSizeInBytes() const
        {
            DCHECK(IsValid());
            return RegisterSizeInBytes() * Count();
        }

    private:
        RegList list_;
        int size_;
        CPURegister::RegisterType type_;

        bool IsValid() const
        {
            constexpr RegList kValidRegisters { 0x8000000ffffffff };
            constexpr RegList kValidVRegisters { 0x0000000ffffffff };
            switch (type_) {
            case CPURegister::kRegister:
                return (list_ & kValidRegisters) == list_;
            case CPURegister::kVRegister:
                return (list_ & kValidVRegisters) == list_;
            case CPURegister::kNoRegister:
                return list_ == 0;
            default:
                UNREACHABLE();
            }
        }
    };

// AAPCS64 callee-saved registers.
#define kCalleeSaved CPURegList::GetCalleeSaved()
#define kCalleeSavedV CPURegList::GetCalleeSavedV()

// AAPCS64 caller-saved registers. Note that this includes lr.
#define kCallerSaved CPURegList::GetCallerSaved()
#define kCallerSavedV CPURegList::GetCallerSavedV()

    // Define a {RegisterName} method for {Register} and {VRegister}.
    DEFINE_REGISTER_NAMES(Register, GENERAL_REGISTERS)
    DEFINE_REGISTER_NAMES(VRegister, VECTOR_REGISTERS)

    // Give alias names to registers for calling conventions.
    constexpr Register kReturnRegister0 = x0;
    constexpr Register kReturnRegister1 = x1;
    constexpr Register kReturnRegister2 = x2;
    constexpr Register kJSFunctionRegister = x1;
    constexpr Register kContextRegister = cp;
    constexpr Register kAllocateSizeRegister = x1;

    constexpr Register kSpeculationPoisonRegister = x23;

    constexpr Register kInterpreterAccumulatorRegister = x0;
    constexpr Register kInterpreterBytecodeOffsetRegister = x19;
    constexpr Register kInterpreterBytecodeArrayRegister = x20;
    constexpr Register kInterpreterDispatchTableRegister = x21;

    constexpr Register kJavaScriptCallArgCountRegister = x0;
    constexpr Register kJavaScriptCallCodeStartRegister = x2;
    constexpr Register kJavaScriptCallTargetRegister = kJSFunctionRegister;
    constexpr Register kJavaScriptCallNewTargetRegister = x3;
    constexpr Register kJavaScriptCallExtraArg1Register = x2;

    constexpr Register kOffHeapTrampolineRegister = ip0;
    constexpr Register kRuntimeCallFunctionRegister = x1;
    constexpr Register kRuntimeCallArgCountRegister = x0;
    constexpr Register kRuntimeCallArgvRegister = x11;
    constexpr Register kWasmInstanceRegister = x7;
    constexpr Register kWasmCompileLazyFuncIndexRegister = x8;

} // namespace internal
} // namespace v8

#endif // V8_ARM64_REGISTER_ARM64_H_
